﻿/**
* FullHitTest
* @author Thomas Vian
* @version 1.0
*/

package com.tomvian.tools
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.BitmapDataChannel;
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.display.Shape;
	import flash.geom.ColorTransform;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.geom.Transform;
	import com.tomvian.maths.Vector2D;

	public class FullHitTest extends Object
	{
		//--------------------------------------------------------------------------
		//
		//  Properties
		//
		//--------------------------------------------------------------------------
		
		private static var turnRed:ColorTransform = new ColorTransform(1, 0, 0, 1, 255, -255, -255, 0);
		
		private static var testCircle:Shape = getTestCircle();
		
		//--------------------------------------------------------------------------
		//
		//  Methods
		//
		//--------------------------------------------------------------------------
		
		private static function getTestCircle():void
		{
			var circ:Shape = new Shape();
			circ.graphics.lineStyle(0, 0, 0);
			circ.graphics.beginFill(0xFF0000);
			circ.graphics.drawCircle(0, 0, 10);
			circ.graphics.endFill();
			
			return circ;
		}
		
		public static function objectBackground(object:DisplayObject, background:DisplayObject):Rectangle
		{
			var objectBounds:Rectangle = object.getBounds(object.stage);
			var objectPoint:Point = new Point(object.x, object.y);
			objectPoint = object.parent.localToGlobal(objectPoint);
			var offCenter:Matrix = new Matrix(1, 0, 0, 1, objectPoint.x - objectBounds.left, objectPoint.y - objectBounds.top);
			offCenter.a = object.transform.concatenatedMatrix.a;
			offCenter.b = object.transform.concatenatedMatrix.b;
			offCenter.c = object.transform.concatenatedMatrix.c;
			offCenter.d = object.transform.concatenatedMatrix.d;
			
			var objectBitmap:BitmapData = new BitmapData(object.width, object.height, true, 0x00000000);
			objectBitmap.draw(object, offCenter);
			
			offCenter.a = offCenter.d = 1;
			offCenter.b = offCenter.c = 0
			
			var backgroundBitmap:BitmapData = new BitmapData(object.width, object.height, true, 0x00000000);
			objectBounds = object.getBounds(background);
			offCenter.tx = -objectBounds.x;
			offCenter.ty = -objectBounds.y;
			backgroundBitmap.draw(background, offCenter, turnRed);
			
			backgroundBitmap.copyChannel(objectBitmap, objectBitmap.rect, new Point(), BitmapDataChannel.ALPHA, BitmapDataChannel.BLUE);
			
			var ret:Rectangle = backgroundBitmap.getColorBoundsRect(0xFFFFFFFF, 0xFFFF00FF);
			
			objectBitmap.dispose();
			backgroundBitmap.dispose();
			
			return ret;
		}
		
		public static function circleBackground(point:Vector2D, radius:Number, background:DisplayObject):Rectangle
		{
			var rad2:Number = radius * 2.0;
			
			testCircle.width = testCircle.height = rad2;
			
			var offCenter:Matrix = new Matrix(1, 0, 0, 1, radius, radius);
			var objectBitmap:BitmapData = new BitmapData(rad2, rad2, true, 0x00000000);
			objectBitmap.draw(testCircle, offCenter);
			
			offCenter.tx = radius + background.x - point.x;
			offCenter.ty = radius + background.y - point.y;
			
			var backgroundBitmap:BitmapData = new BitmapData(rad2, rad2, true, 0x00000000);
			backgroundBitmap.draw(background, offCenter);
			backgroundBitmap.threshold(backgroundBitmap, backgroundBitmap.rect, new Point(), ">", 0x00FFFFFF, 0xFFFF0000);
			backgroundBitmap.copyChannel(objectBitmap, objectBitmap.rect, new Point(), BitmapDataChannel.ALPHA, BitmapDataChannel.BLUE);
			
			//DisplayObjectContainer(background.root).removeChildAt(DisplayObjectContainer(background.root).numChildren - 1);
			//DisplayObjectContainer(background.root).addChild(new Bitmap(backgroundBitmap));
			
			var rect:Rectangle = backgroundBitmap.getColorBoundsRect(0xFFFFFFFF, 0xFFFF00FF);
			
			objectBitmap.dispose();
			backgroundBitmap.dispose();
			
			return rect;
		}
		
		public static function rectBackground(rect:Rectangle, background:DisplayObject):Rectangle
		{
			var offCenter:Matrix = new Matrix(1, 0, 0, 1, -rect.x, -rect.y);
			var backgroundBitmap:BitmapData = new BitmapData(rect.width, rect.height, true, 0x00000000);
			backgroundBitmap.draw(background, offCenter);
			backgroundBitmap.threshold(backgroundBitmap, backgroundBitmap.rect, new Point(), ">", 0x00FFFFFF, 0xFFFF0000);
			
			var rect:Rectangle = backgroundBitmap.getColorBoundsRect(0xFFFFFFFF, 0xFFFF0000);
			
			backgroundBitmap.dispose();
			
			return rect;
		}
		
		public static function objectHitMask(object:DisplayObject, realTest:DisplayObject, hitMask:BitmapData, scale:Number = 1):Rectangle
		{
			if(!INITIALISED) FullHitTest.init();
			
			var objectBounds:Rectangle = object.getBounds(object.stage);
			var objectPoint:Point = new Point(object.x, object.y);
			objectPoint = object.parent.localToGlobal(objectPoint);
			
			var offCenter:Matrix = object.transform.concatenatedMatrix.clone();
			offCenter.tx = (objectPoint.x - objectBounds.left);
			offCenter.ty = (objectPoint.y - objectBounds.top);
			offCenter.scale(scale, scale);
			
			var objectBitmap:BitmapData = new BitmapData(object.width*scale, object.height*scale, true, 0x00000000);
			objectBitmap.draw(object, offCenter);
			
			offCenter.a = offCenter.d = 1;
			offCenter.b = offCenter.c = 0
			
			var backgroundBitmap:BitmapData = new BitmapData(object.width * scale, object.height * scale, true, 0x00000000);
			objectBounds = object.getBounds(realTest);
			offCenter.tx = -(objectBounds.x) * scale;
			offCenter.ty = -(objectBounds.y) * scale;
			backgroundBitmap.draw(hitMask, offCenter, turnRed);
			
			backgroundBitmap.copyChannel(objectBitmap, objectBitmap.rect, new Point(), BitmapDataChannel.ALPHA, BitmapDataChannel.BLUE);
			
			var ret:Rectangle = scaleRectangle(backgroundBitmap.getColorBoundsRect(0xFFFFFFFF, 0xFFFF00FF), 1/scale, true);
			
			objectBitmap.dispose();
			backgroundBitmap.dispose();
			
			return ret;
		}
		
		public static function objectHitMaskScaled(object:DisplayObject, realTest:DisplayObject, hitMask:BitmapData, scale:Number = 1):Rectangle
		{
			if(!INITIALISED) FullHitTest.init();
			
			var objectBounds:Rectangle = object.getBounds(object.stage);
			var objectPoint:Point = new Point(object.x, object.y);
			objectPoint = object.parent.localToGlobal(objectPoint);
			
			var offCenter:Matrix = object.transform.concatenatedMatrix.clone();
			offCenter.tx = (objectPoint.x - objectBounds.left);
			offCenter.ty = (objectPoint.y - objectBounds.top);
			offCenter.scale(scale, scale);
			
			var objectBitmap:BitmapData = new BitmapData(object.width*scale, object.height*scale, true, 0x00000000);
			objectBitmap.draw(object, offCenter);
			
			offCenter.a = offCenter.d = 1;
			offCenter.b = offCenter.c = 0;
			
			var backgroundBitmap:BitmapData = new BitmapData(object.width * scale, object.height * scale, true, 0x00000000);
			objectBounds = object.getBounds(realTest);
			offCenter.tx = -(objectBounds.x) * scale;
			offCenter.ty = -(objectBounds.y) * scale;
			backgroundBitmap.draw(hitMask, offCenter, turnRed);
			
			backgroundBitmap.copyChannel(objectBitmap, objectBitmap.rect, new Point(), BitmapDataChannel.ALPHA, BitmapDataChannel.BLUE);
			
			var ret:Rectangle = scaleRectangle(backgroundBitmap.getColorBoundsRect(0xFFFFFFFF, 0xFFFF00FF), 1/scale, true);
			
			objectBitmap.dispose();
			backgroundBitmap.dispose();
			
			return ret;
		}
		
		public static function normalFromRectangle(object:DisplayObject, rect:Rectangle):Vector2D
		{
			var objectBounds:Rectangle = object.getBounds(object.parent);
			var center:Vector2D = new Vector2D(object.x - objectBounds.left, object.y - objectBounds.top);
			
			if(object["centerOfMass"]) center = center.add(object["centerOfMass"]) as Vector2D;
			
			var rectCenter:Vector2D = new Vector2D(rect.left + rect.width / 2, rect.top + rect.height / 2);
			
			return center.subtract(rectCenter).normalisedMod();
		}
		
		public static function scaleRectangle(rect:Rectangle, scale:Number, aboutCenter:Boolean):Rectangle
		{
			var t_rect:Rectangle = rect.clone();
			t_rect.inflate((scale - 1) * t_rect.width, (scale - 1) * t_rect.height);
			if(aboutCenter) t_rect.offset((scale - 1) * t_rect.left, (scale - 1) * t_rect.top);
			return t_rect;
		}
	}
	
}